package com.listeningframework.boot.autoconfigure.security;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.Assert;

import com.listeningframework.boot.autoconfigure.security.ListeningSecurityProperties.SCHEMA;
import com.listeningframework.boot.autoconfigure.security.overrides.CustomJdbcUserDetailsManagerConfigurer;
import com.listeningframework.boot.autoconfigure.security.overrides.DefaultsJdbcUserDetailsManagerConfigurer;
import com.listeningframework.boot.autoconfigure.security.overrides.ProviderAuthenticationProvider;
import com.listeningframework.boot.autoconfigure.security.overrides.ProviderUserDetailsService;

/**
 * @author listening
 * @version $Id: SpringSecurity.java, v 0.1 2015年7月13日 下午9:58:23 listening Exp $
 * Spring Security Configuration
 */
@Configuration
@ConditionalOnClass({ AuthenticationManager.class, GlobalAuthenticationConfigurerAdapter.class })
@EnableConfigurationProperties(ListeningSecurityProperties.class)
@ConditionalOnProperty(prefix = "listening.security.basic", name = "enabled", havingValue = "true", matchIfMissing = false)
public class SpringSecurityGlobalConfiguration extends GlobalAuthenticationConfigurerAdapter {
	/**
	 * 数据源
	 */
	@Autowired
	private DataSource dataSource;
	/**
	 * DataSource Platform
	 */
	@Value("${spring.datasource.platform}")
	private String datasourcePlatform;
	/**
	 * Spring Security 参数
	 */
	@Autowired
	private ListeningSecurityProperties listeningSecurityProperties;
	/**
	 * 客户Service
	 */
	@Autowired(required = false)
	private ProviderUserDetailsService listeningUserDetailsService;

	@Override
	public void init(AuthenticationManagerBuilder auth) throws Exception {
		Assert.notNull(datasourcePlatform, "DataSource Platform is needed");
		ListeningSecurityProperties.Basic basic = listeningSecurityProperties.getBasic();
		String schema = basic.getSchema().name();
		if (StringUtils.equals(schema, SCHEMA.DEFAULT.name())) {
			defaultSchema(auth);
		} else if (StringUtils.equals(schema, SCHEMA.CUSTOM.name())) {
			customSchema(auth);
		} else if (StringUtils.equals(schema, SCHEMA.PROVIDER.name())) {
			providerSchema(auth);
		}
	}

	/**
	 * 自定义表结认证Provider
	 *
	 * @return ListeningAuthenticationProvider
	 */
	@Bean
	@ConditionalOnProperty(prefix = "listening.security.basic", name = "schema", havingValue = "provider", matchIfMissing = false)
	public ProviderAuthenticationProvider getListeningAuthenticationProvider() {
		Assert.notNull(listeningUserDetailsService, "ListeningUserDetailsService实例缺失");
		return new ProviderAuthenticationProvider().setUserDetailsService(listeningUserDetailsService);
	}

	/**
	 * Default Schema
	 */
	private void defaultSchema(AuthenticationManagerBuilder auth) throws Exception {
		// @formatter:off
		auth.apply(new DefaultsJdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>())
			.withDataSourcePlatform(datasourcePlatform)
			.dataSource(dataSource)
			.withDefaultSchema()
			.passwordEncoder(new BCryptPasswordEncoder());
		// @formatter:on
	}

	/**
	 * Custom Schema
	 */
	private void customSchema(AuthenticationManagerBuilder auth) throws Exception {
		// @formatter:off
		ListeningSecurityProperties.Basic basic = listeningSecurityProperties.getBasic();
		auth.apply(new CustomJdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>())
			.withDatasourcePlatform(datasourcePlatform)
			.dataSource(dataSource)
			.withDefaultSchemaLocation(basic.getCustomSchemaLocation())
			.withDefaultSchemaName(basic.getCustomSchemaName())
			.withDefaultDataName(basic.getCustomDataName())
			.withDefaultSchema(basic.isInitSchema())
			.withDefaultData(basic.isWithDefaultUser())
			.passwordEncoder(new BCryptPasswordEncoder());
		// @formatter:on
	}

	/**
	 * providerSchema
	 */
	private void providerSchema(AuthenticationManagerBuilder auth) {
		auth.authenticationProvider(getListeningAuthenticationProvider());
	}

}
